/*
 * Copyright (c) 2025 Core Devices LLC
 * SPDX-License-Identifier: Apache-2.0
 */

#define DT_DRV_COMPAT sifli_sf32lb_rcc_clk

#include <stdbool.h>
#include <stdint.h>

#include <zephyr/arch/cpu.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/clock_control/sf32lb.h>
#include <zephyr/dt-bindings/clock/sf32lb-clocks-common.h>
#include <zephyr/dt-bindings/clock/sf32lb52x-clocks.h>
#include <zephyr/sys/util.h>
#include <zephyr/toolchain.h>

#include <register.h>

#define HPSYS_CFG_CAU2_CR offsetof(HPSYS_CFG_TypeDef, CAU2_CR)

#define PMUC_HXT_CR1 offsetof(PMUC_TypeDef, HXT_CR1)

#define HPSYS_RCC_CSR    offsetof(HPSYS_RCC_TypeDef, CSR)
#define HPSYS_RCC_CFGR   offsetof(HPSYS_RCC_TypeDef, CFGR)
#define HPSYS_RCC_USBCR  offsetof(HPSYS_RCC_TypeDef, USBCR)
#define HPSYS_RCC_DLL1CR offsetof(HPSYS_RCC_TypeDef, DLL1CR)
#define HPSYS_RCC_DLL2CR offsetof(HPSYS_RCC_TypeDef, DLL2CR)

#define HPSYS_RCC_DLLXCR_EN          HPSYS_RCC_DLL1CR_EN
#define HPSYS_RCC_DLLXCR_STG_Msk     HPSYS_RCC_DLL1CR_STG_Msk
#define HPSYS_RCC_DLLXCR_STG_STEP    24000000UL
#define HPSYS_RCC_DLLXCR_IN_DIV2_EN  HPSYS_RCC_DLL1CR_IN_DIV2_EN
#define HPSYS_RCC_DLLXCR_OUT_DIV2_EN HPSYS_RCC_DLL1CR_OUT_DIV2_EN
#define HPSYS_RCC_DLLXCR_READY       HPSYS_RCC_DLL1CR_READY

#define SF32LB_CLOCK_FREQ_BY_NAME(inst, name)                                                      \
	DT_PROP(DT_INST_CLOCKS_CTLR_BY_NAME(inst, name), clock_frequency)

#define SF32LB_DLL_FREQ(inst, node)                                                                \
	COND_CODE_1(DT_NODE_HAS_STATUS(DT_INST_CHILD(inst, node), okay),                        \
		    (DT_PROP(DT_INST_CHILD(inst, node), clock_frequency)), (0U))

#define SF32LB_DLL1_FREQ(inst) SF32LB_DLL_FREQ(inst, dll1)
#define SF32LB_DLL2_FREQ(inst) SF32LB_DLL_FREQ(inst, dll2)

/* Enum values match the register field encoding used by RCC */
enum sf32lb_sys_clk_idx {
	SF32LB_SYS_CLK_IDX_HRC48 = 0,
	SF32LB_SYS_CLK_IDX_HXT48 = 1,
	SF32LB_SYS_CLK_IDX_LPCLK = 2,
	SF32LB_SYS_CLK_IDX_DLL1 = 3,
};

enum sf32lb_peri_clk_idx {
	SF32LB_PERI_CLK_IDX_HRC48 = 0,
	SF32LB_PERI_CLK_IDX_HXT48 = 1,
};

enum sf32lb_mpi_clk_idx {
	SF32LB_MPI_CLK_IDX_PERI = 0,
	SF32LB_MPI_CLK_IDX_DLL1 = 1,
	SF32LB_MPI_CLK_IDX_DLL2 = 2,
};

enum sf32lb_usb_clk_idx {
	SF32LB_USB_CLK_IDX_SYSCLK = 0,
	SF32LB_USB_CLK_IDX_DLL2 = 1,
};

#define SF32LB_SYS_CLK_SRC_IDX(inst)                                                               \
	DT_ENUM_IDX_OR(DT_DRV_INST(inst), sifli_sys_clk_src, SF32LB_SYS_CLK_IDX_HRC48)
#define SF32LB_PERI_CLK_SRC_IDX(inst)                                                              \
	DT_ENUM_IDX_OR(DT_DRV_INST(inst), sifli_peri_clk_src, SF32LB_PERI_CLK_IDX_HXT48)
#define SF32LB_MPI1_CLK_SRC_IDX(inst)                                                              \
	DT_ENUM_IDX_OR(DT_DRV_INST(inst), sifli_mpi1_clk_src, SF32LB_MPI_CLK_IDX_PERI)
#define SF32LB_MPI2_CLK_SRC_IDX(inst)                                                              \
	DT_ENUM_IDX_OR(DT_DRV_INST(inst), sifli_mpi2_clk_src, SF32LB_MPI_CLK_IDX_PERI)
#define SF32LB_USB_CLK_SRC_IDX(inst)                                                               \
	DT_ENUM_IDX_OR(DT_DRV_INST(inst), sifli_usb_clk_src, SF32LB_USB_CLK_IDX_SYSCLK)

#define SF32LB_SYS_CLK_SRC_VALUE(inst)  SF32LB_SYS_CLK_SRC_IDX(inst)
#define SF32LB_PERI_CLK_SRC_VALUE(inst) SF32LB_PERI_CLK_SRC_IDX(inst)
#define SF32LB_MPI1_CLK_SRC_VALUE(inst) SF32LB_MPI1_CLK_SRC_IDX(inst)
#define SF32LB_MPI2_CLK_SRC_VALUE(inst) SF32LB_MPI2_CLK_SRC_IDX(inst)
#define SF32LB_USB_CLK_SRC_VALUE(inst)  SF32LB_USB_CLK_SRC_IDX(inst)

#define SF32LB_SYS_CLK_FREQ(inst)                                                                  \
	((SF32LB_SYS_CLK_SRC_IDX(inst) == SF32LB_SYS_CLK_IDX_HRC48)                                \
		 ? SF32LB_CLOCK_FREQ_BY_NAME(inst, hrc48)                                          \
	 : (SF32LB_SYS_CLK_SRC_IDX(inst) == SF32LB_SYS_CLK_IDX_HXT48)                              \
		 ? SF32LB_CLOCK_FREQ_BY_NAME(inst, hxt48)                                          \
	 : (SF32LB_SYS_CLK_SRC_IDX(inst) == SF32LB_SYS_CLK_IDX_LPCLK)                              \
		 ? SF32LB_CLOCK_FREQ_BY_NAME(inst, lrc32)                                          \
		 : SF32LB_DLL1_FREQ(inst))

#define SF32LB_PERI_CLK_FREQ(inst)                                                                 \
	((SF32LB_PERI_CLK_SRC_IDX(inst) == SF32LB_PERI_CLK_IDX_HXT48)                              \
		 ? SF32LB_CLOCK_FREQ_BY_NAME(inst, hxt48)                                          \
		 : SF32LB_CLOCK_FREQ_BY_NAME(inst, hrc48))

#define SF32LB_USB_DIV_VALUE(inst) DT_INST_PROP_OR(DT_DRV_INST(inst), sifli_usb_div, 4)

#define SF32LB_RCC_NEEDS_HXT48(inst)                                                               \
	(DT_NODE_HAS_STATUS(DT_INST_CHILD(inst, dll1), okay) ||                                    \
	 DT_NODE_HAS_STATUS(DT_INST_CHILD(inst, dll2), okay) ||                                    \
	 (SF32LB_SYS_CLK_SRC_IDX(inst) == SF32LB_SYS_CLK_IDX_HXT48) ||                             \
	 (SF32LB_PERI_CLK_SRC_IDX(inst) == SF32LB_PERI_CLK_IDX_HXT48))

#define SF32LB_SYS_CLK_REQUIRES_DLL1(inst) (SF32LB_SYS_CLK_SRC_IDX(inst) == SF32LB_SYS_CLK_IDX_DLL1)
#define SF32LB_MPI1_CLK_REQUIRES_DLL2(inst)                                                        \
	(SF32LB_MPI1_CLK_SRC_VALUE(inst) == SF32LB_MPI_CLK_IDX_DLL2)
#define SF32LB_MPI1_CLK_REQUIRES_DLL1(inst)                                                        \
	(SF32LB_MPI1_CLK_SRC_VALUE(inst) == SF32LB_MPI_CLK_IDX_DLL1)
#define SF32LB_MPI2_CLK_REQUIRES_DLL2(inst)                                                        \
	(SF32LB_MPI2_CLK_SRC_VALUE(inst) == SF32LB_MPI_CLK_IDX_DLL2)
#define SF32LB_MPI2_CLK_REQUIRES_DLL1(inst)                                                        \
	(SF32LB_MPI2_CLK_SRC_VALUE(inst) == SF32LB_MPI_CLK_IDX_DLL1)
#define SF32LB_USB_CLK_REQUIRES_DLL2(inst)                                                         \
	(SF32LB_USB_CLK_SRC_VALUE(inst) == SF32LB_USB_CLK_IDX_DLL2)

struct clock_control_sf32lb_rcc_config {
	uintptr_t base;
	uintptr_t cfg;
	uintptr_t pmuc;
	uint8_t hdiv;
	uint8_t pdiv1;
	uint8_t pdiv2;
	uint8_t sys_clk_src;
	uint8_t peri_clk_src;
	uint8_t mpi1_clk_src;
	uint8_t mpi2_clk_src;
	uint8_t usb_clk_src;
	uint8_t usb_div;
	uint32_t sys_clk_freq;
	uint32_t peri_clk_freq;
	uint32_t hrc48_freq;
	uint32_t hxt48_freq;
	uint32_t lrc32_freq;
	uint32_t lrc10_freq;
	uint32_t lxt32_freq;
	uint32_t dll1_freq;
	uint32_t dll2_freq;
	const struct device *hxt48;
};

static inline void configure_dll(const struct device *dev, uint32_t freq, uint32_t dllxcr)
{
	const struct clock_control_sf32lb_rcc_config *config = dev->config;
	uint32_t val;

	/* disable DLLX, clear modified fields */
	val = sys_read32(config->base + dllxcr);
	val &= ~HPSYS_RCC_DLLXCR_EN;
	sys_write32(val, config->base + dllxcr);

	/* configure DLLX */
	val &= ~(HPSYS_RCC_DLLXCR_STG_Msk | HPSYS_RCC_DLLXCR_OUT_DIV2_EN);
	val |= FIELD_PREP(HPSYS_RCC_DLLXCR_STG_Msk, (freq / HPSYS_RCC_DLLXCR_STG_STEP) - 1U) |
	       HPSYS_RCC_DLLXCR_IN_DIV2_EN | HPSYS_RCC_DLLXCR_EN;
	sys_write32(val, config->base + dllxcr);

	do {
		val = sys_read32(config->base + dllxcr);
	} while ((val & HPSYS_RCC_DLLXCR_READY) == 0U);
}

static bool sf32lb_rcc_needs_hxt48(const struct clock_control_sf32lb_rcc_config *config)
{
	return (config->sys_clk_src == SF32LB_SYS_CLK_IDX_HXT48) ||
	       (config->peri_clk_src == SF32LB_PERI_CLK_IDX_HXT48) || (config->dll1_freq != 0U) ||
	       (config->dll2_freq != 0U);
}

static uint32_t sf32lb_get_sys_clk(const struct clock_control_sf32lb_rcc_config *config)
{
	return config->sys_clk_freq;
}

static uint32_t sf32lb_get_hclk(const struct clock_control_sf32lb_rcc_config *config)
{
	uint32_t hdiv = config->hdiv;

	if (hdiv == 0U) {
		return sf32lb_get_sys_clk(config);
	}

	return sf32lb_get_sys_clk(config) / hdiv;
}

static uint32_t sf32lb_get_pclk1(const struct clock_control_sf32lb_rcc_config *config)
{
	uint32_t divisor = BIT(config->pdiv1);

	return sf32lb_get_hclk(config) / MAX(divisor, 1U);
}

static uint32_t sf32lb_get_clk_peri(const struct clock_control_sf32lb_rcc_config *config)
{
	return config->peri_clk_freq;
}

static uint32_t sf32lb_get_mpi_clk(const struct clock_control_sf32lb_rcc_config *config,
				   uint8_t src)
{
	switch (src) {
	case SF32LB_MPI_CLK_IDX_DLL1:
		return config->dll1_freq;
	case SF32LB_MPI_CLK_IDX_DLL2:
		return config->dll2_freq;
	default:
		return config->peri_clk_freq;
	}
}

static uint32_t sf32lb_get_usb_clk(const struct clock_control_sf32lb_rcc_config *config)
{
	uint32_t src = (config->usb_clk_src == SF32LB_USB_CLK_IDX_DLL2)
			       ? config->dll2_freq
			       : sf32lb_get_sys_clk(config);

	if (config->usb_div == 0U) {
		return src;
	}

	return src / config->usb_div;
}

static int clock_control_sf32lb_rcc_on(const struct device *dev, clock_control_subsys_t sys)
{
	const struct clock_control_sf32lb_rcc_config *config = dev->config;
	uint16_t id = *(uint16_t *)sys;

	sys_set_bit(config->base + FIELD_GET(SF32LB_CLOCK_OFFSET_MSK, id),
		    FIELD_GET(SF32LB_CLOCK_BIT_MSK, id));

	return 0;
}

static int clock_control_sf32lb_rcc_off(const struct device *dev, clock_control_subsys_t sys)
{
	const struct clock_control_sf32lb_rcc_config *config = dev->config;
	uint16_t id = *(uint16_t *)sys;

	sys_clear_bit(config->base + FIELD_GET(SF32LB_CLOCK_OFFSET_MSK, id),
		      FIELD_GET(SF32LB_CLOCK_BIT_MSK, id));

	return 0;
}

int clock_control_sf32lb_rcc_get_rate(const struct device *dev, clock_control_subsys_t sys,
				      uint32_t *rate)
{
	const struct clock_control_sf32lb_rcc_config *config = dev->config;
	uint16_t id = *(uint16_t *)sys;

	switch (id) {
	case SF32LB52X_CLOCK_DMAC1:
	case SF32LB52X_CLOCK_EXTDMA:
	case SF32LB52X_CLOCK_EPIC:
	case SF32LB52X_CLOCK_EZIP1:
	case SF32LB52X_CLOCK_LCDC1:
	case SF32LB52X_CLOCK_AES:
	case SF32LB52X_CLOCK_SDMMC1:
	case SF32LB52X_CLOCK_CRC1:
	case SF32LB52X_CLOCK_SECU1:
		*rate = sf32lb_get_hclk(config);
		return 0;
	case SF32LB52X_CLOCK_MPI1:
		*rate = sf32lb_get_mpi_clk(config, config->mpi1_clk_src);
		return 0;
	case SF32LB52X_CLOCK_MPI2:
		*rate = sf32lb_get_mpi_clk(config, config->mpi2_clk_src);
		return 0;
	case SF32LB52X_CLOCK_USBC:
		*rate = sf32lb_get_usb_clk(config);
		return 0;
	case SF32LB52X_CLOCK_MAILBOX1:
	case SF32LB52X_CLOCK_PINMUX1:
	case SF32LB52X_CLOCK_SYSCFG1:
	case SF32LB52X_CLOCK_GPIO1:
	case SF32LB52X_CLOCK_PTC1:
	case SF32LB52X_CLOCK_TRNG:
	case SF32LB52X_CLOCK_EFUSEC:
	case SF32LB52X_CLOCK_GPADC:
	case SF32LB52X_CLOCK_TSEN:
	case SF32LB52X_CLOCK_GPTIM1:
	case SF32LB52X_CLOCK_GPTIM2:
	case SF32LB52X_CLOCK_ATIM1:
		*rate = sf32lb_get_pclk1(config);
		return 0;
	case SF32LB52X_CLOCK_BTIM1:
	case SF32LB52X_CLOCK_BTIM2:
		*rate = sf32lb_get_pclk1(config) / 2U;
		return 0;
	case SF32LB52X_CLOCK_I2C1:
	case SF32LB52X_CLOCK_I2C2:
	case SF32LB52X_CLOCK_I2C3:
	case SF32LB52X_CLOCK_I2C4:
	case SF32LB52X_CLOCK_SPI1:
	case SF32LB52X_CLOCK_SPI2:
	case SF32LB52X_CLOCK_USART2:
	case SF32LB52X_CLOCK_USART3:
		*rate = sf32lb_get_clk_peri(config);
		return 0;
	case SF32LB52X_CLOCK_I2S1:
	case SF32LB52X_CLOCK_AUDPRC:
	case SF32LB52X_CLOCK_AUDCODEC:
	case SF32LB52X_CLOCK_PDM1:
		*rate = config->hxt48_freq;
		return 0;
	default:
		break;
	}

	return -ENOTSUP;
}

static enum clock_control_status clock_control_sf32lb_rcc_get_status(const struct device *dev,
								     clock_control_subsys_t sys)
{
	const struct clock_control_sf32lb_rcc_config *config = dev->config;
	uint16_t id = *(uint16_t *)sys;

	if (sys_test_bit(config->base + FIELD_GET(SF32LB_CLOCK_OFFSET_MSK, id),
			 FIELD_GET(SF32LB_CLOCK_BIT_MSK, id)) != 0) {
		return CLOCK_CONTROL_STATUS_ON;
	}

	return CLOCK_CONTROL_STATUS_OFF;
}

static DEVICE_API(clock_control, clock_control_sf32lb_rcc_api) = {
	.on = clock_control_sf32lb_rcc_on,
	.off = clock_control_sf32lb_rcc_off,
	.get_rate = clock_control_sf32lb_rcc_get_rate,
	.get_status = clock_control_sf32lb_rcc_get_status,
};

static int clock_control_sf32lb_rcc_init(const struct device *dev)
{
	const struct clock_control_sf32lb_rcc_config *config = dev->config;
	bool need_hxt48 = sf32lb_rcc_needs_hxt48(config);
	uint32_t val;
	int ret;

	if (need_hxt48) {
		if ((config->hxt48 == NULL) || !device_is_ready(config->hxt48)) {
			return -ENODEV;
		}

		ret = clock_control_on(config->hxt48, NULL);
		if (ret < 0) {
			return ret;
		}
	}

	if (config->dll1_freq != 0U || config->dll2_freq != 0U) {
		val = sys_read32(config->pmuc + PMUC_HXT_CR1);
		val |= PMUC_HXT_CR1_BUF_DLL_EN;
		sys_write32(val, config->pmuc + PMUC_HXT_CR1);

		val = sys_read32(config->cfg + HPSYS_CFG_CAU2_CR);
		val |= HPSYS_CFG_CAU2_CR_HPBG_EN | HPSYS_CFG_CAU2_CR_HPBG_VDDPSW_EN;
		sys_write32(val, config->cfg + HPSYS_CFG_CAU2_CR);

		val = sys_read32(config->base + HPSYS_RCC_CSR);
		val &= ~(HPSYS_RCC_CSR_SEL_SYS_Msk | HPSYS_RCC_CSR_SEL_PERI_Msk);
		val |= FIELD_PREP(HPSYS_RCC_CSR_SEL_SYS_Msk, SF32LB_SYS_CLK_IDX_HXT48) |
		       FIELD_PREP(HPSYS_RCC_CSR_SEL_PERI_Msk, SF32LB_PERI_CLK_IDX_HXT48);
		sys_write32(val, config->base + HPSYS_RCC_CSR);

		if (config->dll1_freq != 0U) {
			configure_dll(dev, config->dll1_freq, HPSYS_RCC_DLL1CR);
		}

		if (config->dll2_freq != 0U) {
			configure_dll(dev, config->dll2_freq, HPSYS_RCC_DLL2CR);
		}
	}

	/* configure HDIV/PDIV1/PDIV2 dividers */
	val = sys_read32(config->base + HPSYS_RCC_CFGR);
	val &= ~(HPSYS_RCC_CFGR_HDIV_Msk | HPSYS_RCC_CFGR_PDIV1_Msk | HPSYS_RCC_CFGR_PDIV2_Msk);
	val |= FIELD_PREP(HPSYS_RCC_CFGR_HDIV_Msk, config->hdiv) |
	       FIELD_PREP(HPSYS_RCC_CFGR_PDIV1_Msk, config->pdiv1) |
	       FIELD_PREP(HPSYS_RCC_CFGR_PDIV2_Msk, config->pdiv2);
	sys_write32(val, config->base + HPSYS_RCC_CFGR);

	val = sys_read32(config->base + HPSYS_RCC_CSR);
	val &= ~(HPSYS_RCC_CSR_SEL_SYS_Msk | HPSYS_RCC_CSR_SEL_PERI_Msk |
		 HPSYS_RCC_CSR_SEL_MPI1_Msk | HPSYS_RCC_CSR_SEL_MPI2_Msk |
		 HPSYS_RCC_CSR_SEL_USBC_Msk);
	val |= FIELD_PREP(HPSYS_RCC_CSR_SEL_SYS_Msk, config->sys_clk_src) |
	       FIELD_PREP(HPSYS_RCC_CSR_SEL_PERI_Msk, config->peri_clk_src) |
	       FIELD_PREP(HPSYS_RCC_CSR_SEL_MPI1_Msk, config->mpi1_clk_src) |
	       FIELD_PREP(HPSYS_RCC_CSR_SEL_MPI2_Msk, config->mpi2_clk_src) |
	       FIELD_PREP(HPSYS_RCC_CSR_SEL_USBC_Msk, config->usb_clk_src);
	sys_write32(val, config->base + HPSYS_RCC_CSR);

	val = FIELD_PREP(HPSYS_RCC_USBCR_DIV_Msk, config->usb_div);
	sys_write32(val, config->base + HPSYS_RCC_USBCR);

	return 0;
}

/* DLL1/2 requires HXT48 */
IF_ENABLED(UTIL_OR(
		DT_NODE_HAS_STATUS(DT_INST_CHILD(0, dll1), okay),
		DT_NODE_HAS_STATUS(DT_INST_CHILD(0, dll2), okay)),
	   (BUILD_ASSERT(
		(DT_NODE_HAS_STATUS(DT_INST_CLOCKS_CTLR_BY_NAME(0, hxt48), okay)),
		"DLL1/2 require HXT48 to be enabled");))

/* DLL1/2 frequency must be a multiple of step size */
IF_ENABLED(DT_NODE_HAS_STATUS(DT_INST_CHILD(0, dll1), okay), (
	   BUILD_ASSERT(
		((DT_PROP(DT_INST_CHILD(0, dll1), clock_frequency) != 0) &&
		 ((DT_PROP(DT_INST_CHILD(0, dll1), clock_frequency) %
		   HPSYS_RCC_DLLXCR_STG_STEP) == 0)),
		"DLL1 frequency must be a non-zero multiple of "
		STRINGIFY(HPSYS_RCC_DLLXCR_STG_STEP)
	   );))

IF_ENABLED(DT_NODE_HAS_STATUS(DT_INST_CHILD(0, dll2), okay), (
	   BUILD_ASSERT(
		((DT_PROP(DT_INST_CHILD(0, dll2), clock_frequency) != 0) &&
		 ((DT_PROP(DT_INST_CHILD(0, dll2), clock_frequency) %
		   HPSYS_RCC_DLLXCR_STG_STEP) == 0)),
		"DLL2 frequency must be a non-zero multiple of "
		STRINGIFY(HPSYS_RCC_DLLXCR_STG_STEP)
	   );))

BUILD_ASSERT(!SF32LB_RCC_NEEDS_HXT48(0) ||
		     DT_NODE_HAS_STATUS(DT_INST_CLOCKS_CTLR_BY_NAME(0, hxt48), okay),
	     "HXT48 clock must be enabled when selected or when DLLs are used");

BUILD_ASSERT(!SF32LB_SYS_CLK_REQUIRES_DLL1(0) || DT_NODE_HAS_STATUS(DT_INST_CHILD(0, dll1), okay),
	     "DLL1 system clock selection requires the dll1 node to be enabled");

BUILD_ASSERT(!SF32LB_MPI1_CLK_REQUIRES_DLL2(0) || DT_NODE_HAS_STATUS(DT_INST_CHILD(0, dll2), okay),
	     "MPI1 clock selection requires the dll2 node to be enabled when set to DLL2");

BUILD_ASSERT(!SF32LB_MPI1_CLK_REQUIRES_DLL1(0) || DT_NODE_HAS_STATUS(DT_INST_CHILD(0, dll1), okay),
	     "MPI1 clock selection requires the dll1 node to be enabled when set to DLL1");

BUILD_ASSERT(!SF32LB_MPI2_CLK_REQUIRES_DLL2(0) || DT_NODE_HAS_STATUS(DT_INST_CHILD(0, dll2), okay),
	     "MPI2 clock selection requires the dll2 node to be enabled when set to DLL2");

BUILD_ASSERT(!SF32LB_MPI2_CLK_REQUIRES_DLL1(0) || DT_NODE_HAS_STATUS(DT_INST_CHILD(0, dll1), okay),
	     "MPI2 clock selection requires the dll1 node to be enabled when set to DLL1");

BUILD_ASSERT(IN_RANGE(SF32LB_USB_DIV_VALUE(0), 1, 7),
	     "USB clock divider must be in the range [1, 7]");

BUILD_ASSERT(!SF32LB_USB_CLK_REQUIRES_DLL2(0) || DT_NODE_HAS_STATUS(DT_INST_CHILD(0, dll2), okay),
	     "USB clock selection requires the dll2 node to be enabled when set to DLL2");

static const struct clock_control_sf32lb_rcc_config config = {
	.base = DT_REG_ADDR(DT_INST_PARENT(0)),
	.cfg = DT_REG_ADDR(DT_INST_PHANDLE(0, sifli_cfg)),
	.pmuc = DT_REG_ADDR(DT_INST_PHANDLE(0, sifli_pmuc)),
	.hxt48 = DEVICE_DT_GET_OR_NULL(DT_INST_CLOCKS_CTLR_BY_NAME(0, hxt48)),
	.hdiv = DT_INST_PROP(0, sifli_hdiv),
	.pdiv1 = DT_INST_PROP(0, sifli_pdiv1),
	.pdiv2 = DT_INST_PROP(0, sifli_pdiv2),
	.sys_clk_src = SF32LB_SYS_CLK_SRC_VALUE(0),
	.peri_clk_src = SF32LB_PERI_CLK_SRC_VALUE(0),
	.mpi1_clk_src = SF32LB_MPI1_CLK_SRC_VALUE(0),
	.mpi2_clk_src = SF32LB_MPI2_CLK_SRC_VALUE(0),
	.usb_clk_src = SF32LB_USB_CLK_SRC_VALUE(0),
	.usb_div = SF32LB_USB_DIV_VALUE(0),
	.sys_clk_freq = SF32LB_SYS_CLK_FREQ(0),
	.peri_clk_freq = SF32LB_PERI_CLK_FREQ(0),
	.hrc48_freq = SF32LB_CLOCK_FREQ_BY_NAME(0, hrc48),
	.hxt48_freq = SF32LB_CLOCK_FREQ_BY_NAME(0, hxt48),
	.lrc32_freq = SF32LB_CLOCK_FREQ_BY_NAME(0, lrc32),
	.lrc10_freq = SF32LB_CLOCK_FREQ_BY_NAME(0, lrc10),
	.lxt32_freq = SF32LB_CLOCK_FREQ_BY_NAME(0, lxt32),
	.dll1_freq = COND_CODE_1(DT_NODE_HAS_STATUS(DT_INST_CHILD(0, dll1), okay),
				 (DT_PROP(DT_INST_CHILD(0, dll1), clock_frequency)), (0U)),
		 .dll2_freq = COND_CODE_1(DT_NODE_HAS_STATUS(DT_INST_CHILD(0, dll2), okay),
				 (DT_PROP(DT_INST_CHILD(0, dll2), clock_frequency)), (0U)),
};

DEVICE_DT_INST_DEFINE(0, clock_control_sf32lb_rcc_init, NULL, NULL, &config, PRE_KERNEL_1,
		      CONFIG_CLOCK_CONTROL_INIT_PRIORITY, &clock_control_sf32lb_rcc_api);
